home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / New System Software Extensions / QuickDraw™ GX v1.0ß2 / Sample Code / Printing Samples / Printer Drivers… / LaserWriterIISC / SCPrinterStatus.c < prev    next >
Encoding:
Text File  |  1993-09-13  |  27.3 KB  |  818 lines  |  [TEXT/MPS ]

  1. /*---------------------------------------------------------------------------
  2. FILENAME
  3.     SCPrinterStatus.c
  4.  
  5. DESCRIPTION
  6.     This file contains the routines which manage checking the status of the 
  7.     SC printer and alerting the user (as needed) to problems that might arise.
  8.         
  9.     9/13/93 - dmh - Updated for the b2 seed.    
  10.     
  11. COPYRIGHT
  12.     Copyright Apple Computer, Inc. 1989-1992
  13.     All rights reserved. 
  14.     
  15. INTERFACE ROUTINES:
  16.     SetPrinterProblemStatResIndex
  17.     SetPrinterProblemStatResID
  18.     FindPrinterProblem
  19.     ResolvePrinterProblem
  20.  
  21. --------------------------------------------------------------------------- */
  22.  
  23. // Include the standard Mac header files 
  24. #include "MacIncludes.h"
  25.  
  26. // Include the new QuickDraw GX graphics header files 
  27. #include <graphics routines.h>
  28. #include <graphics libraries.h>
  29. #include <math routines.h>
  30.  
  31. // Include the required Printing Manager header files 
  32. #include <PrintingManager.h>
  33. #include <PrintingMessages.h>
  34. #include <PrintingDrivers.h>
  35. #include <Collections.h>
  36. #include <Messages.h>
  37. #include <PrintingResTypes.h>
  38. #include <PrintingErrors.h>
  39.  
  40. #include <Exceptions.h>
  41.  
  42. // Include the internal driver constants and types used by this module 
  43. #include "Resources.h"
  44. #include "LaserSCIntf.h"
  45. #include "SCPrinterStatus.h"
  46.  
  47.  
  48. /***************************************************************************************
  49. *                                         INTERNAL ROUTINES                                                     *
  50. ***************************************************************************************/                        
  51.  
  52. /****************************************************************************************
  53.  
  54.                             CheckEngineStatus1Bits
  55.  
  56.                             
  57.     function:
  58.                 This routine is called to examine the Engine Status 1 Bits in the status
  59.                 data returned from the printer.  The routine examines these bits to 
  60.                 determine if any of the following problems exists on the printer:
  61.  
  62.                                 Fixing assembly has malfunctioned
  63.                                 Laser has malfunctioned
  64.                                 Polygon motor has malfunctioned
  65.                                 Serial communications have malfunctioned
  66.                                 Some other hardware problem has occurred
  67.  
  68.                 If one of these problems is encountered, the problemStatResIndex parameter
  69.                 returns an index into a 'stat' resource of the status that reflects the 
  70.                 printer's current status. 
  71.                 
  72.     parameters:    
  73.                 pSenseData                Printer status info. received from the printer
  74.                 problemStatResIndex    returns the 'stat' index of the printer's current error
  75.                                             status if an error was encountered
  76.     returns:
  77.                 None
  78.     
  79. ****************************************************************************************/
  80. void CheckEngineStatus1Bits(SCSenseDataPtr pSenseData, short *problemStatResIndex)
  81. {
  82.     if ( FixingMalfunction(pSenseData) )
  83.         *problemStatResIndex = kBadPrintFixStatIdx;
  84.     else
  85.     if ( LaserMalfunction(pSenseData) )
  86.         *problemStatResIndex = kBadLaserStatIdx;
  87.     else
  88.     if ( PolyMalfunction(pSenseData) )
  89.         *problemStatResIndex = kBadPolyMotorStatIdx;
  90.     else
  91.     if ( SerialMalfunction(pSenseData) )
  92.         *problemStatResIndex = kBadSerialStatIdx;
  93.     else
  94.         *problemStatResIndex = kGenericBadHdwareStatIdx;
  95. }
  96. /* CheckEngineStatus1Bits */
  97.  
  98.  
  99. /****************************************************************************************
  100.  
  101.                             CheckEngineStatus0Bits
  102.  
  103.                             
  104.     function:    
  105.                 This routine is called to examine the Engine Status 0 Bits in the status
  106.                 data returned from the printer.  The routine examines these bits to 
  107.                 determine if any of the following problems exists on the printer:
  108.  
  109.                                 No toner cartridge/improperly installed
  110.                                 No paper tray/manual feed and no paper in printer
  111.                                 No paper cartridge/improperly installed
  112.                                 Paper jam
  113.                                 Upper door of printer is open
  114.                                 Test printing is underway
  115.                                 Fixing unit is being heated
  116.  
  117.                 If one of these problems is encountered, the problemStatResIndex parameter
  118.                 returns an index into a 'stat' resource of the status that reflects the 
  119.                 printer's current status. 
  120.                 
  121.     parameters:    
  122.                 pSenseData                Printer status info. received from the printer
  123.                 problemStatResIndex    returns the 'stat' index of the printer's current error
  124.                                             status if an error was encountered
  125.     returns:
  126.                 None
  127.     
  128. ****************************************************************************************/
  129. void CheckEngineStatus0Bits(SCSenseDataPtr pSenseData, short *problemStatResIndex)
  130. {
  131.     if ( NoToner(pSenseData) )
  132.         *problemStatResIndex = kBadTonerCartridgeStatIdx;
  133.     else
  134.     if ( NoPaperTray(pSenseData) )
  135.         *problemStatResIndex = kBadPaperCartridgeStatIdx;
  136.     else
  137.     if ( NoPaper(pSenseData) )
  138.         *problemStatResIndex = kOutOfPaperStatIdx;
  139.     else
  140.     if ( PaperJam(pSenseData) )
  141.         *problemStatResIndex = kPaperJamStatIdx;
  142.     else
  143.     if ( DoorOpen(pSenseData) )
  144.         *problemStatResIndex = kOpenDoorStatIdx;
  145.     else
  146.     if ( TestPrint(pSenseData) )
  147.         *problemStatResIndex = kPrintTestStatIdx;
  148.     else
  149.     if ( HeatingFixUnit(pSenseData) )
  150.         *problemStatResIndex = kHeatFixingUnitStatIdx;
  151. }
  152. /* CheckEngineStatus0Bits */
  153.  
  154.  
  155. /****************************************************************************************
  156.  
  157.                             SetStatusIndex
  158.  
  159.                             
  160.     function:
  161.                 This routine is called to initialize the statResId and statResIndex fields
  162.                 in the status record referenced by pStatus.  If the dontAlert parameter
  163.                 is true, the we make sure that only informationalStatus 'stat' resources 
  164.                 will be referenced by pStatus.
  165.                 
  166.     parameters:                
  167.                 pStatus                Status record used to alert the user to printer problems
  168.                 currentProblem        the 'stat' ID and res index of the printer's current status
  169.                 manualFeed            T => doing manual feed job; F => doing auto-feed
  170.                 dontAlert            T => we don't want to set the status record's statResId and
  171.                                         statResIndex to a value which would cause the PFE to display
  172.                                         an alert dialog to the user.  When true, we make sure only
  173.                                         informationalStatus 'stat' resources will be referenced by
  174.                                         pStatus.
  175.                 
  176.     returns:
  177.                 None
  178.                 
  179. ****************************************************************************************/
  180. void SetStatusIndex( gxStatusRecord         *pStatus, 
  181.                     long                 printerProblem, 
  182.                     Boolean             manualFeed,
  183.                     Boolean                dontAlert)
  184. {
  185.     short        whichStatResID;
  186.     short        whichStatResIndex;
  187.     
  188.     // Extract the statResID and statResIndex values from the printerProblem parameter
  189.  
  190.     whichStatResID = GetStatResID(printerProblem);
  191.     whichStatResIndex = GetStatResIndex(printerProblem);
  192.     
  193.     // The normal settings of statResID and statResIndex are just the values in printerProblem
  194.     
  195.     pStatus->statResId         = whichStatResID;
  196.     pStatus->statResIndex     = whichStatResIndex;
  197.     
  198.     // Do we need to override the normal settings because we want to suppress an alert or
  199.     // use an alert that's already provided by the Printing Manager?
  200.     
  201.     if (whichStatResID == kTransmissionStatID)
  202.     {
  203.         switch (whichStatResIndex)
  204.         {
  205.             case kOutOfPaperStatIdx:
  206.                 if (dontAlert)    // T => Don't cause alert to be displayed
  207.                 {
  208.                     pStatus->statResIndex = kWaitingForPaperStatIdx;
  209.                 }
  210.                 else    // T => We want alert to be displayed; use system supplied alert
  211.                 {
  212.                     pStatus->statResId = gxUnivAlertStatusResourceId;
  213.                     pStatus->statResIndex = (manualFeed) ? gxUnivManualFeedIndex : gxUnivOutOfPaperIndex;
  214.                 }
  215.                 break;
  216.  
  217.             case kPaperJamStatIdx:
  218.                 if (!dontAlert)    // T => We want alert to be displayed; use system supplied alert
  219.                 {
  220.                     pStatus->statResId = gxUnivAlertStatusResourceId;
  221.                     pStatus->statResIndex = gxUnivPaperJamIndex;
  222.                 }
  223.                 break;
  224.             
  225.             case kBadPaperCartridgeStatIdx:
  226.                 if (!dontAlert)    // T => We want alert to be displayed; use system supplied alert
  227.                 {
  228.                     pStatus->statResId = gxUnivAlertStatusResourceId;
  229.                     pStatus->statResIndex = gxUnivNoPaperTrayIndex;
  230.                 }
  231.                 break;
  232.             
  233.             case kOpenDoorStatIdx:
  234.                 if (dontAlert)    // T => We want alert to be displayed; use our alernative non-alert status
  235.                 {
  236.                     pStatus->statResIndex = kOpenDoorNoAlertStatIdx;
  237.                 }
  238.                 break;
  239.             
  240.             case kBadTonerCartridgeStatIdx:
  241.                 if (dontAlert)    // T => We want alert to be displayed; use our alernative non-alert status
  242.                 {
  243.                     pStatus->statResIndex = kBadTonerCartridgeNoAlertStatIdx;
  244.                 }
  245.                 break;
  246.         }
  247.     }
  248. }
  249. /* SetStatusIndex */
  250.  
  251.  
  252. /****************************************************************************************
  253.  
  254.                             SetStatusBuffer
  255.  
  256.                             
  257.     function:
  258.                 This routine is called to initialize the contents of the status buffer
  259.                 in the status record referenced by pStatus.  If we're doing a manual feed
  260.                 job and the printer is out of paper, then we set the buffer area to be an
  261.                 appropriately initialized gxManualFeedRecord structure. If it's not manual feed,
  262.                 but the printer is out of paper, then we set buffer area to be a structure of
  263.                 size gxOutOfPaperStatusBufferSize.  
  264.                 
  265.     parameters:                
  266.                 pStatus                Status record used to alert the user to printer problems
  267.                 currentProblem        the 'stat' ID and res index of the printer's current status
  268.                 manualFeed            T => doing manual feed job; F => doing auto-feed
  269.                 thePaperType        the paper type of the paper that should be loaded into the printer
  270.                 
  271.     returns:
  272.                 None
  273.     
  274. ****************************************************************************************/
  275. void SetStatusBuffer(gxStatusRecord        *pStatus, 
  276.                     long         printerProblem, 
  277.                     Boolean     manualFeed,
  278.                     gxPaperType    thePaperType)
  279. {
  280.     // Determine what info needs to be added to the status record's buffer area
  281.     
  282.     if ( GetStatResIndex(printerProblem) == kOutOfPaperStatIdx )
  283.     {
  284.         if (manualFeed)    // T => Fill in the gxManualFeedRecord structure with the appropriate paper type info.
  285.         {
  286.             gxManualFeedRecord    *mfeedInfo;
  287.  
  288.             mfeedInfo = (gxManualFeedRecord *) &pStatus->statusBuffer;
  289.             mfeedInfo->canAutoFeed = true;
  290.             GXGetPaperTypeName(thePaperType, mfeedInfo->paperTypeName);
  291.         }
  292.         else    //    T => Fill in the gxOutOfPaperRecord structure with the appropriate info.
  293.         {
  294.             gxOutOfPaperRecord    *outOfPaperInfo;
  295.  
  296.             outOfPaperInfo = (gxOutOfPaperRecord *) &pStatus->statusBuffer;
  297.             GXGetPaperTypeName(thePaperType, outOfPaperInfo->paperTypeName);
  298.         }
  299.     }
  300.     // else - For all other errors, the status buffer area will contain a univStatusBuffer structure
  301. }
  302. /* SetStatusBuffer */
  303.  
  304.  
  305. /****************************************************************************************
  306.  
  307.                             ClearStatus
  308.  
  309.                             
  310.     function:
  311.                 This routine is called to tell the Printing Finder Extension (PFE) to dismiss the
  312.                 alert we had displayed.  It signals the PFE to dismiss the alert by updating
  313.                 the device status to "ready".  Anytime the PFE receives a ready status, it
  314.                 automatically dismisses the dialog.
  315.                 
  316.     parameters:    
  317.                 pStatus        Status record to use in call to GXAlertTheUser
  318.                 
  319.     returns:
  320.                 OSErr
  321.     
  322. ****************************************************************************************/
  323. OSErr ClearStatus(gxStatusRecord     *pStatus)
  324. {
  325.     OSErr        anErr;
  326.     short        saveResult = pStatus->dialogResult;
  327.     
  328.     // So that the PFE highlights the ok button before dismissing the dialog, set the
  329.     // dialog result to "ok".
  330.     pStatus->dialogResult = ok;
  331.     
  332.     pStatus->statResId      =    gxUnivAlertStatusResourceId;
  333.     pStatus->statResIndex =    gxUnivPrinterReadyIndex;
  334.     
  335.     // Tell the PFE to remove the alert
  336.     anErr = GXAlertTheUser(pStatus);
  337.     
  338.     // Restore the previous dialog result
  339.     pStatus->dialogResult = saveResult;
  340.  
  341.     return(anErr);
  342. }
  343. /* ClearStatus */
  344.  
  345.  
  346. /****************************************************************************************
  347.  
  348.                             CheckPrinter
  349.  
  350.                             
  351.     function:
  352.                 This routine queries the printer to determine its current status.  It returns,
  353.                 in the currentProblem parameter, a resource ID and res index into a 'stat' 
  354.                 resource of the status that reflects the printer's currentstatus.  The high 
  355.                 word of currentProblem contains the 'stat' resource ID of the 'stat' resource 
  356.                 that indicates the current printer problem, and the low word contains the 
  357.                 index within the 'stat' resource of the specific printer problem.
  358.                 
  359.     parameters:    
  360.                 pStatus                Status record used for alerting the user to the printer problem
  361.                 currentProblem        returns the 'stat' ID and res index of the printer's current status
  362.                 abortPrinting        returns true if printer problem is fatal; false otherwise
  363.                 
  364.     returns:
  365.                 OSErr
  366.     
  367. ****************************************************************************************/
  368.  
  369. OSErr CheckPrinter(    gxStatusRecord         *pStatus,
  370.                     long                *currentProblem,
  371.                     Boolean                *abortPrinting)
  372. {
  373.     OSErr        anErr;
  374.     short        printerStatus;
  375.     long        oldProblem;
  376.  
  377.     *abortPrinting = false;
  378.     
  379.     // Remember the state of the printer from the last time we statused it
  380.     oldProblem = *currentProblem;
  381.  
  382.     // Issue a "Test Unit Ready" command to the printer so that we can get the latest
  383.     // printer status info. when we query it (via a call to FindPrinterProblem).
  384.     
  385.     anErr = LaserSC_GetDeviceStatus(&printerStatus);
  386.     if (anErr == noErr)
  387.     {
  388.         // Is the printer still not ready?
  389.         if (printerStatus != kGoodCondition)
  390.         {
  391.             // Status the printer to determine its current error condition state
  392.             anErr = FindPrinterProblem(currentProblem, abortPrinting);
  393.             if (anErr == noErr)
  394.             {
  395.                 // If we've encountered a change in status or the printer is now suffering
  396.                 // a fatal error (e.g. hardware problem), then dump the current status alert
  397.                 // and return the new status
  398.                 
  399.                 if ( (oldProblem != *currentProblem) || *abortPrinting )
  400.                 {
  401.                     // Tell the Printing Finder Extension to dismiss the current alert
  402.                     anErr = ClearStatus(pStatus);
  403.                 }
  404.                 // else - no change in the status and it's not a fatal error
  405.             }
  406.             // else - can't status the printer
  407.         }
  408.         else    //    T => The printer is now ready
  409.             *currentProblem = kNoPrinterProblem;
  410.     }
  411.     // else - can't issue a "Test Unit Ready" command to the printer
  412.     
  413.     check(anErr == noErr);
  414.     return(anErr);
  415. }
  416. /* CheckPrinter */
  417.  
  418.  
  419. /***************************************************************************************
  420. *                                         INTERFACE ROUTINES                                                     *
  421. ***************************************************************************************/                        
  422.  
  423. /****************************************************************************************
  424.  
  425.                             SetPrinterProblemStatResIndex
  426.  
  427.                             
  428.     function:
  429.                 This is a utility routine for stuffing a 'stat' resource index into the 
  430.                 low word of a printer problem long word variable.  The high word of a printer
  431.                 problem variable contains the 'stat' resource ID of the 'stat' resource that
  432.                 indicates the current printer problem, and the low word contains the 
  433.                 index within the 'stat' resource of the specific printer problem.
  434.                 
  435.     parameters:                
  436.                 printerProblem        returns long word with statResIndex in the lower word
  437.                 statResIndex        'stat' resource index value to stuff into printerProblem
  438.  
  439.     returns:
  440.                 None
  441.     
  442. ****************************************************************************************/
  443. void SetPrinterProblemStatResIndex(short statResIndex, long *printerProblem)
  444. {
  445.     short    *pShort;
  446.     
  447.     pShort = ((short *) printerProblem) + 1;
  448.     *pShort = statResIndex;
  449. }
  450. /* SetPrinterProblemStatResIndex */
  451.  
  452.  
  453. /****************************************************************************************
  454.  
  455.                             SetPrinterProblemStatResID
  456.  
  457.                             
  458.     function:
  459.                 This is a utility routine for stuffing a 'stat' resource ID into the 
  460.                 high word of a printer problem long word variable.  The high word of a printer
  461.                 problem variable contains the 'stat' resource ID of the 'stat' resource that
  462.                 indicates the current printer problem, and the low word contains the 
  463.                 index within the 'stat' resource of the specific printer problem.
  464.                 
  465.     parameters:                
  466.                 printerProblem        returns long word with statResID in the high word
  467.                 statResID            'stat' resource ID value to stuff into printerProblem
  468.  
  469.     returns:
  470.                 None
  471.     
  472. ****************************************************************************************/
  473. void SetPrinterProblemStatResID(short statResID, long *printerProblem)
  474. {
  475.     short    *pShort;
  476.     
  477.     pShort = (short *) printerProblem;
  478.     *pShort = statResID;
  479. }
  480. /* SetPrinterProblemStatResID */
  481.  
  482.  
  483. /****************************************************************************************
  484.  
  485.                             FindPrinterProblem
  486.  
  487.                             
  488.     function:
  489.                 This routine is called when the driver has discovered that the LaserWriter SC
  490.                 is no longer ready.  It calls this routine to determine the reason why the
  491.                 printer is not ready.  This routine queries the printer to get the latest
  492.                 "sense data" from the printer.  It then returns a resource ID and res index 
  493.                 into a 'stat' resource of the status that reflects the printer's current 
  494.                 status.  This index is returned in the printerProblem parameter.  The high 
  495.                 word of printerProblem contains the 'stat' resource ID of the 'stat' resource 
  496.                 that indicates the current printer problem, and the low word contains the 
  497.                 index within the 'stat' resource of the specific printer problem.
  498.                 
  499.                 If the abortPrinting error is non-nil, then this routine will set it to true if 
  500.                 the error state encountered is one for which we abort printing.
  501.                 
  502.     parameters:                
  503.                 printerProblem        returns the 'stat' ID and res index of the printer's current status
  504.                 abortPrinting        if non-nil, sets to true if printer status causes printing to abort
  505.  
  506.     returns:
  507.                 OSErr
  508.     
  509. ****************************************************************************************/
  510. OSErr FindPrinterProblem(long *printerProblem,  Boolean *abortPrinting)
  511. {
  512.     OSErr                anErr;
  513.     SCSenseData         senseData;
  514.     short                problemStatResID = kNoPrinterProblem;
  515.     short                problemStatResIndex = kNoPrinterProblem;
  516.  
  517.     if (abortPrinting != nil)    // Pointer specified so initialize it 
  518.         *abortPrinting = false;
  519.  
  520.     // Get the latest status information from the printer 
  521.     anErr = LaserSC_GetSenseData(&senseData);
  522.     
  523.     if (anErr == noErr)
  524.     {
  525.         // Is the printer out of paper? 
  526.         if ( (senseData.senseKey & kEndOfMedium) == kEndOfMedium )
  527.         {
  528.             problemStatResID = kTransmissionStatID;
  529.             problemStatResIndex = kOutOfPaperStatIdx;
  530.         }
  531.         else
  532.         {
  533.             // What sort of error did we encounter? 
  534.             switch ( SenseKey(senseData) )
  535.             {
  536.                 case kNotReady:
  537.                     CheckEngineStatus0Bits(&senseData, &problemStatResIndex);
  538.                     
  539.                     if (problemStatResIndex != kNoPrinterProblem)
  540.                         problemStatResID = kTransmissionStatID;
  541.                     break;
  542.                     
  543.                 case kMediumError:
  544.                     problemStatResID = kTransmissionStatID;
  545.                     problemStatResIndex = kPaperJamStatIdx;
  546.                     break;
  547.                 
  548.                 case kHardwareError:
  549.                     CheckEngineStatus1Bits(&senseData, &problemStatResIndex);
  550.                     
  551.                     if (problemStatResIndex != kNoPrinterProblem)
  552.                         problemStatResID = kEngineStatusStatID;
  553.  
  554.                     if (abortPrinting != nil)    // Pointer specified so set it 
  555.                         *abortPrinting = true;
  556.                     break;
  557.  
  558.                 case kIllegalRequest:
  559.                     check(0);                // Illegal command - should never happen in debugged software 
  560.                     break;
  561.                 
  562.                 case kUnitAttention:
  563.                     // Check the controller status bits for a specific error. If none, flag that the 
  564.                     // printer reset itself (because it did). 
  565.                     
  566.                     if ( (senseData.controlStatus & kRamFailMask) == kRamFailMask )
  567.                     {
  568.                         problemStatResID = kEngineStatusStatID;
  569.                         problemStatResIndex = kDRamFailureStatIdx;
  570.  
  571.                         if (abortPrinting != nil)    // Pointer specified so set it 
  572.                             *abortPrinting = true;
  573.                     }
  574.                     else
  575.                     if ( (senseData.controlStatus & kEngineFailMask) == kEngineFailMask )
  576.                     {
  577.                         problemStatResID = kEngineStatusStatID;
  578.                         problemStatResIndex = kLaserInitFailureStatIdx;
  579.  
  580.                         if (abortPrinting != nil)    // Pointer specified so set it 
  581.                             *abortPrinting = true;
  582.                     }
  583.                     break;
  584.     
  585.             } // switch 
  586.         }
  587.     }
  588.  
  589.     // Update the printerProblem parameter with the proper status indicators 
  590.     
  591.     SetPrinterProblemStatResIndex(problemStatResIndex, printerProblem);
  592.     SetPrinterProblemStatResID(problemStatResID, printerProblem);
  593.     
  594.     return(anErr);
  595. }
  596. /* FindPrinterProblem */
  597.  
  598.  
  599. /****************************************************************************************
  600.  
  601.                             ResolvePrinterProblem
  602.  
  603.                             
  604.     function:
  605.                 This routine is called to alert the user to a problem with the printer.  The
  606.                 problem may be one the user can resolve (e.g. out of paper) or one which
  607.                 cannot be resolved easily (e.g. hardware problem).  For the user correctable
  608.                 problems, the user is alerted to the problem and can either fix the problem
  609.                 and continue the print job, or cancel the print job.  In the case of the
  610.                 non-resolvable problems, the user is only given the choice of aborting the
  611.                 print job.  Also, if a fatal error has occurred, we abort the print job.
  612.                 
  613.                 The routine uses the GXAlertTheUser Printing Manager call to display the 
  614.                 alerts to the user.  Whenever possible, we try to use a system defined
  615.                 alert (e.g. manual feed) as opposed to one provided by this driver.  An
  616.                 alert is displayed until all of the printer's problems have been resolved
  617.                 or the user aborted printing.
  618.                 
  619.     parameters:    
  620.                 printerProblem        high word contains 'stat' res ID corresponding to problem
  621.                                         low word contains 'stat' res index corresponding to problem
  622.                 manualFeed            T => doing manual feed job; F => doing auto-feed job
  623.                 thePaperType        the paper type for the paper that should be placed into
  624.                                         the printer for manual feed jobs; nil if not manual feed
  625.                 dialogResult        result (item hit) returned from the dialog when it was dismissed
  626.                 
  627.     returns:
  628.                 OSErr
  629.     
  630. ****************************************************************************************/
  631. OSErr ResolvePrinterProblem(     long             printerProblem, 
  632.                                 Boolean         manualFeed,
  633.                                 gxPaperType        thePaperType,
  634.                                 short            *dialogResult)
  635. {
  636.  
  637.     OSErr                    anErr = noErr;
  638.     OSErr                    anErr2;
  639.     gxStatusRecord            *pStatus;
  640.     gxJob                    theJob = GXGetJob();
  641.     Boolean                    abortPrinting = false;
  642.     long                    oldProblem;
  643.     long                    maxStatusBuffSz;
  644.     
  645.     // Make sure we only display an alert if there has been an error
  646.     require(printerProblem != kNoPrinterProblem, NoPrinterProblem);
  647.     
  648.     // Allocate a status record large enough to handle status with the largest buffer size. 
  649.     // Currently the largest record is the max. of the univStatusBuffer, gxManualFeedRecord, and gxOutOfPaperRecord structures. 
  650.  
  651.     maxStatusBuffSz = gxOutOfPaperStatusBufferSize;
  652.                 
  653.     pStatus = (gxStatusRecord *) NewPtrClear(sizeof(gxStatusRecord) + maxStatusBuffSz);
  654.     anErr = MemError();
  655.     require(anErr == noErr, NewPtrClear);
  656.  
  657.     // Initialize the misc. fields within the status record 
  658.     pStatus->bufferLen = maxStatusBuffSz;
  659.     
  660.     // Now we loop with an alert displayed until all the problems are resolved, the
  661.     // user has aborted the print job, or a fatal error has occurred.
  662.     do
  663.     {
  664.         // Initialize the status record's statResID and statResIndex fields based upon the error encountered
  665.         SetStatusIndex(pStatus, printerProblem, manualFeed, false);
  666.         
  667.         // Initialize the contents of the status record buffer based upon the error encountered
  668.         SetStatusBuffer(pStatus, printerProblem, manualFeed, thePaperType);
  669.         
  670.         // Make sure the dialogResult has been reset from some previously displayed dialog (if any) 
  671.         pStatus->dialogResult = nil;
  672.         
  673.         // Display the initial error alert and see if the problem resolves itself while alerting
  674.         do
  675.         {
  676.             // Tell the Printing Manager to display the selected alert
  677.  
  678.             anErr = GXAlertTheUser(pStatus);
  679.             if (anErr != noErr)
  680.                 break;
  681.             
  682.             // Remember the last printer problem we experienced
  683.             oldProblem = printerProblem;
  684.  
  685.             if (pStatus->dialogResult == nil)    //    T => No user button action yet
  686.             {
  687.                 // Check to see if the printer problem has gone away or is now a different problem
  688.                 
  689.                 anErr = CheckPrinter(pStatus, &printerProblem, &abortPrinting);
  690.                 if (anErr != noErr)
  691.                     break;
  692.                 
  693.                 // If we've now experienced a fatal printer problem (e.g. bad hardware) that is different
  694.                 // than the original problem, then alert the user to the problem.
  695.                 
  696.                 if ( abortPrinting && (oldProblem != printerProblem) )
  697.                 {
  698.                     // Make sure the previous alert has been dismissed by the Printing Finder Extension
  699.                     
  700.                     anErr = ClearStatus(pStatus);
  701.                     require(anErr == noErr, ClearStatus1);
  702.  
  703.                     // Initialize the status record's statResID and statResIndex fields based upon the abort error
  704.                     SetStatusIndex(pStatus, printerProblem, manualFeed, false);
  705.                     
  706.                     // Initialize the contents of the status record buffer based upon the abort error
  707.                     SetStatusBuffer(pStatus, printerProblem, manualFeed, thePaperType);
  708.         
  709.                     // Make sure the dialogResult has been reset from the previously displayed dialog
  710.                     pStatus->dialogResult = nil;
  711.         
  712.                     // Display the new fatal error alert until the user acknowledges it
  713.                     do
  714.                     {
  715.                         anErr = GXAlertTheUser(pStatus);
  716.                     }
  717.                     while ( (pStatus->dialogResult == nil) && (anErr == noErr) );
  718.                 }
  719.                 // else - not a fatal error or the printer problem hasn't changed
  720.             }
  721.             // else - user hasn't clicked any button yet
  722.         }
  723.         while ((pStatus->dialogResult == nil) && (oldProblem == printerProblem) && (anErr == noErr) );
  724.  
  725.         // At this point there has been some resolution to the problem.  The user responded to the
  726.         // alert, the printer problem was fixed at the printer, we got an error checking the status
  727.         // of the printer, or we got an error trying to alert the user. We must always clear status 
  728.         // to make sure that the alert is dismissed.  Then check for errors.
  729.  
  730.         anErr2 = ClearStatus(pStatus);
  731.         if (anErr2 != noErr)
  732.         {
  733.             if (anErr == noErr)
  734.                 anErr == anErr2;
  735.             require(anErr2 == noErr, ClearStatus2);
  736.         }
  737.         
  738.         // If the user has cancelled printing, set the user abort error
  739.         if (pStatus->dialogResult == cancel)
  740.         {
  741.             anErr = gxPrUserAbortErr;
  742.             require(anErr == noErr, UserAborted);
  743.         }
  744.  
  745.         // Did we have a problem statusing the printer or alerting the user?
  746.         require(anErr == noErr, InitialAlertFails);
  747.         
  748.         // Was the original error we displayed a fatal printer problem?
  749.         require(!abortPrinting, PrinterDied);
  750.  
  751.         // If we are in manual feed mode and the user has decided to switch to automatic feed
  752.         // then we will exit.  The problem has basically gone away and we'll catch any new problems
  753.         // when the calling routine re-checks to make sure the printer is ready.
  754.  
  755.         if (    (manualFeed) &&
  756.                 (GetStatResIndex(printerProblem) == kOutOfPaperStatIdx) && 
  757.                 (pStatus->dialogResult == gxAutoFeedButtonId)
  758.             )
  759.             printerProblem = kNoPrinterProblem;
  760.                 
  761.  
  762.         // If the user has dismissed the alert but the same problem still remains, we sit 
  763.         // in a loop until either the printer problem is resolved or another problem arises.  Here 
  764.         // the status record passed to GXAlertTheUser is guaranteed to not generate alerts, because
  765.         // the 'stat' item referenced is not of type userAttention or userAlert.  
  766.  
  767.         if ((printerProblem != kNoPrinterProblem) && (oldProblem == printerProblem))
  768.         {
  769.             // Initialize the status record's statResID and statResIndex fields based upon the abort error
  770.             SetStatusIndex(pStatus, printerProblem, manualFeed, true);
  771.             
  772.             // Update the PFE with the new status
  773.             
  774.             anErr = GXAlertTheUser(pStatus);
  775.             require(anErr == noErr, GXAlertTheUser);
  776.  
  777.             // Keep statusing the printer until the problem is fixed or the problem changes
  778.             do
  779.             {
  780.                 anErr = CheckPrinter(pStatus, &printerProblem, &abortPrinting);
  781.                 require(anErr == noErr, CheckPrinter);
  782.  
  783.                 // Let the client app have a chance to run
  784.                 anErr = GXJobIdle();
  785.                 require(anErr == noErr, JobIdleFails);
  786.             }
  787.             while ( (printerProblem != kNoPrinterProblem) && (oldProblem == printerProblem) );
  788.         }
  789.         // else - no longer a printer problem or now it's a different problem
  790.     }
  791.     while (printerProblem != kNoPrinterProblem);
  792.  
  793.     // Make sure the result of displaying the alert is returned
  794.     *dialogResult = pStatus->dialogResult;
  795.  
  796.  
  797. /******* Clean-up *******/
  798.  
  799. JobIdleFails:
  800. CheckPrinter:
  801. GXAlertTheUser:
  802. InitialAlertFails:
  803. UserAborted:
  804. ClearStatus2:
  805. ClearStatus1:
  806.     DisposPtr((Ptr) pStatus);
  807.     
  808. NewPtrClear:
  809. NoPrinterProblem:
  810.     return(anErr);
  811.     
  812. PrinterDied:
  813.     DisposPtr((Ptr) pStatus);
  814.     return(gxPrIOAbortErr);
  815. }
  816. /* ResolvePrinterProblem */
  817.  
  818.